/*
 * @Author: 13177709051 3506463075@qq.com
 * @Date: 2024-12-02 15:41:29
 * @LastEditors: 13177709051 3506463075@qq.com
 * @LastEditTime: 2024-12-02 18:47:15
 * @FilePath: /example/timewheel.cpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <iostream>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <functional>
#include <memory>
#include <unistd.h>

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
class TimerTask{
    private:
        uint64_t _id;       // 定时器任务对象ID
        uint32_t _timeout;  //定时任务的超时时间
        bool _canceled;     // false-表示没有被取消， true-表示被取消
        TaskFunc _task_cb;  //定时器对象要执行的定时任务
        ReleaseFunc _release; //用于删除TimerWheel中保存的定时器对象信息
    public:
        TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb): 
            _id(id), _timeout(delay), _task_cb(cb), _canceled(false) {}
        ~TimerTask() { 
            if (_canceled == false) _task_cb(); 
            _release(); 
        }
        void Cancel() { _canceled = true; }
        void SetRelease(const ReleaseFunc &cb) { _release = cb; }
        uint32_t DelayTime() { return _timeout; }
};

class TimerWheel {
    private:
        using PtrTask = std::shared_ptr<TimerTask>;
        using WeakTask = std::weak_ptr<TimerTask>;
        int _tick;      //当前的秒针，走到哪里释放哪里，释放哪里，就相当于执行哪里的任务
        int _capacity;  //表盘最大数量---其实就是最大延迟时间
        std::vector<std::vector<PtrTask>> _wheel;
        std::unordered_map<uint64_t, WeakTask> _timers;
    private:
        void RemoveTimer(uint64_t id) {
            auto it = _timers.find(id);
            if (it != _timers.end()) {
                _timers.erase(it);
            }
        }
    public:
        TimerWheel():_capacity(60), _tick(0), _wheel(_capacity) {}
        void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb) {
            PtrTask pt(new TimerTask(id, delay, cb));
            pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
            int pos = (_tick + delay) % _capacity;
            _wheel[pos].push_back(pt);
            _timers[id] = WeakTask(pt);
        }
        //刷新/延迟定时任务
        void TimerRefresh(uint64_t id) {
            //通过保存的定时器对象的weak_ptr构造一个shared_ptr出来，添加到轮子中
            auto it = _timers.find(id);
            if (it == _timers.end()) {
                return;//没找着定时任务，没法刷新，没法延迟
            }
            PtrTask pt = it->second.lock();//lock获取weak_ptr管理的对象对应的shared_ptr
            int delay = pt->DelayTime();
            int pos = (_tick + delay) % _capacity;
            _wheel[pos].push_back(pt);
        }
        void TimerCancel(uint64_t id) {
            auto it = _timers.find(id);
            if (it == _timers.end()) {
                return;//没找着定时任务，没法刷新，没法延迟
            }
            PtrTask pt = it->second.lock();
            if (pt) pt->Cancel();
        }
        //这个函数应该每秒钟被执行一次，相当于秒针向后走了一步
        void RunTimerTask() {
            _tick = (_tick + 1) % _capacity;
            _wheel[_tick].clear();//清空指定位置的数组，就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
        }
};

class Test {
    public:
        Test() {std::cout << "构造" << std::endl;}
        ~Test() {std::cout << "析构" << std::endl;}
};

void DelTest(Test *t) {
    delete t;
}

int main()
{
    TimerWheel tw;

    Test *t = new Test();

    tw.TimerAdd(888, 5, std::bind(DelTest, t));

    for(int i = 0; i < 5; i++) {
        sleep(1);
        tw.TimerRefresh(888);//刷新定时任务
        tw.RunTimerTask();//向后移动秒针
        std::cout << "刷新了一下定时任务，重新需要5s中后才会销毁\n";
    }
    // tw.TimerCancel(888);
    while(1) {
        sleep(1);
        std::cout << "-------------------\n";
        tw.RunTimerTask();//向后移动秒针
    }
    return 0;
}

// 这⾥，我们就⽤到了智能指针shared_ptr，shared_ptr有个计数器，当计数为0的时候，才会真正释放
// ⼀个对象，那么如果连接在第10s进⾏了⼀次通信，则我们继续向定时任务中，添加⼀个30s后（也就
// 是第40s）的任务类对象的shared_ptr，则这时候两个任务shared_ptr计数为2，则第30s的定时任务
// 被释放的时候，计数-1，变为1，并不为0，则并不会执⾏实际的析构函数，那么就相当于这个第30s的
// 任务失效了，只有在第40s的时候，这个任务才会被真正释放。


// TimerTask 代表一个定时任务，每个任务有一个唯一的 ID，一个超时时间 timeout，一个定时任务的回调函数 task_cb，以及任务的取消状态 canceled。
// 成员变量：
// _id：定时任务的唯一标识符。
// _timeout：任务的超时时间（单位：秒），即任务从当前时间起过多少秒触发。
// _canceled：表示任务是否已经被取消。如果为 true，表示任务已取消，定时器不再执行该任务。
// _task_cb：定时任务的回调函数，定时器到期后将会执行该函数。
// _release：当任务被取消或者完成后，负责从定时器轮中移除该任务的回调函数。
// 主要方法：
// TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb)：构造函数，初始化任务的 ID、超时时间和回调函数。
// ~TimerTask()：析构函数，执行回调函数，如果任务未被取消，还会执行任务的回调 _task_cb()。同时调用 _release 来移除该任务。
// Cancel()：取消定时任务，将 _canceled 设置为 true，任务不会再执行。
// SetRelease(const ReleaseFunc &cb)：设置任务释放时的回调函数，通常是从定时器轮中移除任务。
// DelayTime()：返回任务的超时时间。

// TimerWheel 是定时器轮的核心类，负责管理所有的定时任务，并根据当前的时间（即“秒针”位置）触发定时任务。定时器轮的结构包含了多个环节：时间轮、任务管理和定时任务的处理。
// 成员变量：
// _tick：当前时间轮的“秒针”，即指向当前时间的槽位。每秒钟更新一次。
// _capacity：时间轮的容量（槽位数量），即时间轮上可以存放多少个定时任务。这里的设定是 60，表示每分钟有 60 个槽位。
// _wheel：一个二维向量，用来存储定时任务。每个槽位保存一个任务队列，任务根据超时时间映射到不同的槽位。
// _timers：一个哈希表，使用任务 ID 作为键，weak_ptr 作为值。weak_ptr 是对任务对象的弱引用，避免任务在定时器执行时被销毁。
// 主要方法：
// TimerWheel()：构造函数，初始化时间轮的容量为 60，并初始化槽位数组 _wheel。
// TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb)：添加定时任务到时间轮。
// 创建一个 TimerTask 对象，并将其添加到时间轮的正确槽位中。
// 通过计算任务的超时时间与当前的时间 tick 位置来确定任务应该放入时间轮的哪个槽位。
// 使用 weak_ptr 保证任务对象在任务不再需要时能被自动销毁。
// TimerRefresh(uint64_t id)：刷新或延迟定时任务。
// 根据任务的 ID 查找定时任务，通过 weak_ptr 获取 shared_ptr，然后重新将任务加入到新的槽位中。
// 如果找不到该任务，则不做任何处理。
// TimerCancel(uint64_t id)：取消定时任务。
// 根据任务的 ID 查找任务，调用 Cancel() 方法来取消任务，停止任务的执行。
// RunTimerTask()：每秒钟调用一次，相当于“秒针”前进。它会清空当前槽位的任务（即触发该槽位上所有任务的回调函数）。
// 每次更新 _tick，并清除当前槽位的所有任务，意味着所有超时的任务会被执行。



// 内存管理详解：std::shared_ptr 和 std::weak_ptr
// 在这段代码中，std::shared_ptr 和 std::weak_ptr 被用来管理 TimerTask 对象的生命周期，确保内存能够正确地释放，且避免内存泄漏或循环引用等问题。为了更好地理解这个部分，我们需要深入了解这两种智能指针的工作原理。
// 1. std::shared_ptr（共享智能指针）
// std::shared_ptr 是一种 引用计数型 智能指针，它通过维护一个内部的引用计数来管理资源（在本例中是 TimerTask 对象）的生命周期。std::shared_ptr 会确保指向的对象在没有其他 shared_ptr 指向它时自动销毁。
// 工作原理：
// 每次创建一个 std::shared_ptr 时，它会初始化一个引用计数，初始值为 1。
// 每当一个新的 std::shared_ptr 拷贝或赋值时，引用计数会增加 1。
// 当一个 std::shared_ptr 被销毁时（例如，离开作用域或者显式调用 reset()），引用计数会减少 1。
// 当最后一个 shared_ptr 被销毁时，引用计数变为 0，std::shared_ptr 会自动释放它所管理的对象的内存。
// 应用到 TimerTask 中：
// 在这段代码中，TimerTask 对象通过 std::shared_ptr<TimerTask> 被管理。当创建一个新的定时任务时（在 TimerAdd 函数中），任务会被包装在一个 std::shared_ptr 中，并存储在时间轮的槽位中。这样，只有在所有 shared_ptr 被销毁时，TimerTask 才会被自动释放。
// 2. std::weak_ptr（弱智能指针）
// std::weak_ptr 是与 std::shared_ptr 配对使用的智能指针，它与 std::shared_ptr 的主要区别是 不增加引用计数。std::weak_ptr 主要用于避免循环引用的问题。它不会影响对象的生命周期，因此它不会阻止对象被销毁。
// 工作原理：
// std::weak_ptr 不能直接访问或修改所管理的对象；它只是提供了一个 弱引用。
// std::weak_ptr 的主要作用是避免 循环引用，即防止两个对象互相持有对方的 shared_ptr，从而导致引用计数永远不为 0，造成内存泄漏。
// std::weak_ptr 可以通过 lock() 方法将其转换为 std::shared_ptr，只有当 shared_ptr 对象依然存在时，lock() 才会返回一个有效的 shared_ptr，否则返回一个空的 shared_ptr。
// 应用到 TimerTask 中：
// 在这段代码中，std::weak_ptr<TimerTask> 用来存储定时任务的引用，它与 std::shared_ptr<TimerTask> 不同，不会增加任务对象的引用计数。它只用于 监听任务是否还存在。
// _timers 哈希表：_timers 用来存储定时任务的 ID 和对应的 std::weak_ptr。当任务被添加到时间轮时，std::weak_ptr 被插入到 _timers 中，这样即使任务从时间轮的槽位中移除，_timers 中的 weak_ptr 也不会导致任务对象被“提前销毁”。
// 通过 std::weak_ptr，当我们需要访问任务时（例如刷新任务，或取消任务），我们可以通过 lock() 来尝试获取 shared_ptr。如果任务已经被销毁（例如任务超时并被执行），lock() 返回一个空的 shared_ptr，否则返回有效的 shared_ptr。
// 3. 如何避免内存泄漏或循环引用
// 1) 内存泄漏避免
// std::shared_ptr 会自动管理资源，确保 TimerTask 对象在没有任何 shared_ptr 引用时被销毁。但是，如果 std::shared_ptr 与 std::weak_ptr 配合不当，仍然可能会导致内存泄漏。
// 例如，如果一个任务在时间轮中已经过期并被执行，但是 std::shared_ptr 仍然持有对该任务的引用，那么即使任务已经执行，任务对象也不会被销毁。std::weak_ptr 的引入正是为了避免这种情况。
// std::weak_ptr 允许我们在不增加引用计数的情况下追踪对象的生命周期。当一个任务被移除或取消时，std::weak_ptr 不会持有该任务的引用计数，这样就可以确保 shared_ptr 在没有引用时自动销毁任务对象。